﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using Cofe.Core.Property;

namespace Cofe.Core.Implements
{
    public class PropertyExpressionDefinitionBase : IPropertyExpressionDefinitions
    {
        public PropertyExpressionDefinitionBase(Type enumType)
        {
            Category = enumType;
        }

        protected Dictionary<object, object> _expressionsT =
            new Dictionary<object, object>();

        public Type Category { get; private set; }

        /// <summary>
        /// Register ORM based properties, which stored in CofeSystemInfo or it's subclasses.
        /// </summary>
        /// <param name="property"></param>
        /// <param name="expression"></param>
        protected void registerPropertyExpression(object property, Expression<Func<CofeSystemInfo, object>> expression)
        {
            _expressionsT.Add(property, expression);
        }

        protected void registerPropertyExpression<T>(object property, Expression<Func<CofeSystemInfo, T>> expression)
        {
            _expressionsT.Add(property, expression);
        }

        #region getPropertyExpression
                      
        private object getExtendedPropertyExpression<T>(object property)
        {
            Type defaultType = DecoratorExtension.GetDefaultType(property);
            if (defaultType != typeof(T))
                throw new ArgumentException(String.Format("GetPropertyExpression - Wrong Type {0} != {1}", defaultType, typeof(T)));
            switch (defaultType.ToString())
            {
                case "System.String":
                    Expression<Func<CofeSystemInfo, string>> retVal1 = 
                        e => (e.DatabaseEntry.ExtendedProperties.OfType<PropertyPairString>()
                              .FirstOrDefault(p => p.PropertyId == (int)property)).ORMValue;
                    return retVal1;
                case "System.Double":
                    Expression<Func<CofeSystemInfo, Double>> retVal9 =
                        e => (e.DatabaseEntry.ExtendedProperties.OfType<PropertyPairDouble>()
                            .FirstOrDefault(p => p.PropertyId == (int)property)).ORMValue;
                    return retVal9;
                case "System.Int16":
                    Expression<Func<CofeSystemInfo, Int16>> retVal2 =
                        e => (e.DatabaseEntry.ExtendedProperties.OfType<PropertyPairInt16>()
                            .FirstOrDefault(p => p.PropertyId == (int)property)).ORMValue;
                    return retVal2;
                case "System.Int32":
                    Expression<Func<CofeSystemInfo, Int32>> retVal3 =
                        e => (e.DatabaseEntry.ExtendedProperties.OfType<PropertyPairInt32>()
                            .FirstOrDefault(p => p.PropertyId == (int)property)).ORMValue;
                    return retVal3;
                case "System.Int64":
                    Expression<Func<CofeSystemInfo, Int64>> retVal4 =
                        e => (e.DatabaseEntry.ExtendedProperties.OfType<PropertyPairInt64>()
                            .FirstOrDefault(p => p.PropertyId == (int)property)).ORMValue;
                    return retVal4;
                case "System.Boolean":
                    Expression<Func<CofeSystemInfo, Boolean>> retVal5 =
                        e => (e.DatabaseEntry.ExtendedProperties.OfType<PropertyPairBool>()
                            .FirstOrDefault(p => p.PropertyId == (int)property)).ORMValue;
                    return retVal5;
                case "System.DateTime":
                    Expression<Func<CofeSystemInfo, DateTime>> retVal6 =
                        e => (e.DatabaseEntry.ExtendedProperties.OfType<PropertyPairDateTime>()
                            .FirstOrDefault(p => p.PropertyId == (int)property)).ORMValue;
                    return retVal6;
                case "System.Guid":
                    Expression<Func<CofeSystemInfo, Guid>> retVal7 =
                        e => (e.DatabaseEntry.ExtendedProperties.OfType<PropertyPairGuid>()
                            .FirstOrDefault(p => p.PropertyId == (int)property)).ORMValue;
                    return retVal7;
                case "System.Byte[]":
                    Expression<Func<CofeSystemInfo, byte[]>> retVal8 =
                        e => (e.DatabaseEntry.ExtendedProperties.OfType<PropertyPairByteArray>()
                            .FirstOrDefault(p => p.PropertyId == (int)property)).ORMValue;
                    return retVal8;
            }
            throw new NotSupportedException();
        }
        #endregion

        public virtual Expression<Func<CofeSystemInfo, PropertyPair>> GetPropertyPairExpression(object property)
        {
            if (DecoratorExtension.GetDatabaseCacheOption(property) == DatabaseCacheOptions.CachablePropertyPairs)
            {
                return e => (e.DatabaseEntry.ExtendedProperties
                                .FirstOrDefault(p => p.PropertyId == (int)property));
            }
            else return null;
        }

        public virtual Expression<Func<CofeSystemInfo, T>> GetPropertyExpression<T>(object property)            
        {
            if (_expressionsT.ContainsKey(property))
            {
                if (!(_expressionsT[property] is Expression<Func<CofeSystemInfo, T>>))
                    throw new ArgumentException(String.Format("Wrong type for {0}, require {1}, but found {2}", property, typeof(T), _expressionsT[property].GetType()));

                return _expressionsT[property] as Expression<Func<CofeSystemInfo, T>>;
            }

            if (DecoratorExtension.GetDatabaseCacheOption(property) == DatabaseCacheOptions.CachablePropertyPairs)
            {
                string alias = CofeServices.PropertyDictionary.GetPropertyString(property);
                return (Expression<Func<CofeSystemInfo, T>>) getExtendedPropertyExpression<T>(property);           
            }
            throw new NotSupportedException("The property has not defined it's property expresssion, and DatabaseCacheOptions != CachablePropertyPairs");
        }       

        public IEnumerable<object> GetSupportedProperties()
        {
            return _expressionsT.Keys;
        }

        public override string ToString()
        {
            return String.Format("ExpressionDefinition: {0} with {1} items", Category, _expressionsT.Count);
        }

    }

    public class PropertyExpressionDefinitionBase<T> : PropertyExpressionDefinitionBase
    {
        public PropertyExpressionDefinitionBase()
            : base(typeof(T))
        {
        }

    }
}
